/**
 * @description excel公式计算第二版
 * @author Zengwe
 */
import { TableTools } from '../utils/excel';
type TExpArr = ({type: number, val: any} | string | number | boolean | (number | string)[])[];
export enum ValueType {
    num = 10,
    str = 20,
    operate = 30,
    split = 35,
    cell = 40,
    boolean,
    array,
}
// @dynamic
export class Calculate {
    static instance: Calculate;
    static mode: 'prod'|'test' = 'prod'
    static getInstance() {
        if (Calculate.instance == undefined) {
            Calculate.instance = new Calculate();
        }
        return Calculate.instance;
    }
    protected OperatePriority: { [key: string]: { [key: string]: (exp: string[], idx: number, ...param: any[]) => number } } = {
        '35': {
            'PLUS(': (exp: TExpArr, idx) => {
                exp.splice(idx - 1, 2, exp[idx - 1]);
                return idx - 2;
            },
            'MINUX(': (exp: any, idx) => {
                exp[idx - 1].val = -exp[idx - 1].val;
                exp.splice(idx - 1, 2, exp[idx - 1]);
                return idx - 2;
            }
        },
        '10': {
            '>': (exp: TExpArr, idx) => {
                return this.OperatePriority['10']['_judgeBoolean'].call(null, <any>exp, idx, '>');
            },
            '>=': (exp: TExpArr, idx) => {
                return this.OperatePriority['10']['_judgeBoolean'].call(null, <any>exp, idx, '>=');
            },
            '<': (exp: TExpArr, idx) => {
                return this.OperatePriority['10']['_judgeBoolean'].call(null, <any>exp, idx, '<');
            },
            '<=': (exp: TExpArr, idx: number) => {
                return this.OperatePriority['10']['_judgeBoolean'].call(null, <any>exp, idx, '<=');
            },
            '==': (exp: TExpArr, idx: number) => {
                return this.OperatePriority['10']['_judgeBoolean'].call(null, <any>exp, idx, '==');
            },
            '_judgeBoolean': (exp: any, idx, operate: '>' | '>=' | '<' | '<=' | '==') => {
                let result = false;
                let firstValue = exp[idx - 2].val;
                let scondtValue = exp[idx - 1].val;
                switch (operate) {
                    case '>':
                        if (firstValue > scondtValue) {
                            result = true;
                        }
                        break;
                    case '>=':
                        if (firstValue >= scondtValue) {
                            result = true;
                        }
                        break;
                    case '<':
                        if (firstValue < scondtValue) {
                            result = true;
                        }
                        break;
                    case '<=':
                        if (firstValue <= scondtValue) {
                            result = true;
                        }
                        break;
                    case '==':
                        if (firstValue == scondtValue) {
                            result = true;
                        }
                        break;
                }
                exp.splice(idx - 2, 3, {type: ValueType.boolean, val: result});
                return idx - 3;
            },
            ':': (exp: TExpArr, idx) => {
                return idx;
            }
        },
        '20': {
            '+': (exp: TExpArr, idx) => {
                return this.OperatePriority['30']['_mathGenerateCalcu'].call(null, <any>exp, idx, '+');
            },
            '-': (exp: TExpArr, idx) => {
                return this.OperatePriority['30']['_mathGenerateCalcu'].call(null, <any>exp, idx, '-');
            }
        },
        '30': {
            '*': (exp: TExpArr, idx) => {
                return this.OperatePriority['30']['_mathGenerateCalcu'].call(null, <any>exp, idx, '*');
            },
            '/': (exp: TExpArr, idx) => {
                return this.OperatePriority['30']['_mathGenerateCalcu'].call(null, <any>exp, idx, '/');
            },
            '_mathGenerateCalcu': (exp: any[], idx, operate: '+' | '-' | '*' | '/') => {
                let value: number[] = [];
                let firstValueType = Object.prototype.toString.call(exp[idx - 2].val);
                let scondValyeType = Object.prototype.toString.call(exp[idx - 1].val);
                let firstValue: any[] = [];
                let scondValue: any[] = [];
                if (firstValueType !== '[object Array]' && scondValyeType !== '[object Array]') {
                    firstValue = [{type: ValueType.num, val: exp[idx - 2].val}];
                    scondValue = [{type: ValueType.num, val: exp[idx - 1].val}];
                } else if (firstValueType == '[object Array]' && scondValyeType == '[object Array]') {
                    firstValue = exp[idx - 2].val;
                    scondValue = exp[idx - 1].val;
                } else if (firstValueType == '[object Array]' || scondValyeType == '[object Array]') {
                    if (firstValueType == '[object Array]') {
                        firstValue = exp[idx - 2].val;
                        firstValue.forEach(ele => {
                            scondValue.push(exp[idx - 1].val);
                        });
                    }
                    if (scondValyeType == '[object Array]') {
                        scondValue = exp[idx - 1].val;
                        scondValue.forEach(ele => {
                            firstValue.push(exp[idx - 2]);
                        });
                    }
                }
                let operateFunc = {
                    add: (v1: string|number, v2: string|number) => {
                        return this.accAdd(this.parseValue(v1), this.parseValue(v2));
                    },
                    subtract: (v1: string|number, v2: string|number) => {
                        return this.accSubtr(this.parseValue(v1), this.parseValue(v2));
                    },
                    multiplication: (v1: string|number, v2: string|number) => {
                        return this.accMul(this.parseValue(v1), this.parseValue(v2));
                    },
                    division: (v1: string|number, v2: string|number) => {
                        return this.accDiv(this.parseValue(v1), this.parseValue(v2));
                    }
                }
                let operateName: 'add' | 'subtract' | 'multiplication' | 'division' = 'add'
                switch (operate) {
                    case '+':
                        operateName = 'add';
                        break;
                    case '-':
                        operateName = 'subtract';
                        break;
                    case '*':
                        operateName = 'multiplication';
                        break;
                    case '/':
                        operateName = 'division';
                        break;
                }
                for (let i = 0; i < firstValue.length; i++) {
                    value.push(operateFunc[operateName](firstValue[i].val, scondValue[i].val));
                }
                if (value.length > 1) {
                    exp.splice(idx - 2, 3, {type: ValueType.num, val: value});
                } else {
                    exp.splice(idx - 2, 3,  {type: ValueType.num, val: value[0]});
                }
                return idx - 3;
            }
        },
        '40': {
            ',': (exp: TExpArr, idx) => { return idx }
        },
        '50': {
            'IF(': (exp: any, idx) => {
                if (exp[idx - 5].val == true) {
                    exp.splice(idx - 5, 6, exp[idx - 4]);
                } else {
                    exp.splice(idx - 5, 6, exp[idx - 3]);
                }
                return idx - 6;
            },
            'AND(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_AND_OR'].call(null, <any>exp, idx, 'AND');
            },
            'OR(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_AND_OR'].call(null, <any>exp, idx, 'OR');
            },
            '_AND_OR': (exp: any, idx, operate: 'AND' | 'OR') => {
                let result = undefined;
                if (operate == 'OR') {
                    result = false;
                } else if (operate == 'AND') {
                    result = true;
                }
                let count = 1;
                let total = 0;
                let mark = idx;
                while (count > 0) {
                    mark--;
                    if (exp[mark].type == ValueType.operate && exp[mark].val == ',') {
                        count++;
                        total++;
                    } else {
                        if (operate == "AND") {
                            if (exp[mark].val === false) {
                                result = false;
                                break;
                            };
                        }
                        if (operate == 'OR') {
                            if (exp[mark].val === true) {
                                result = true;
                                break;
                            };
                        }
                        count--;
                    }
                }
                let allCount = (total + 1) * 2;
                exp.splice(idx - (allCount - 1), allCount, { type: ValueType.boolean, val: result});
                return idx - allCount;
            },
            'SUM(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_SUM_AVG_MAX_MIN'].call(null, <any>exp, idx, 'SUM');
            },
            'AVG(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_SUM_AVG_MAX_MIN'].call(null, <any>exp, idx, 'AVG');
            },
            'AVERAGE(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_SUM_AVG_MAX_MIN'].call(null, <any>exp, idx, 'AVG');
            },
            'MAX(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_SUM_AVG_MAX_MIN'].call(null, <any>exp, idx, 'MAX');
            },
            'MIN(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_SUM_AVG_MAX_MIN'].call(null, <any>exp, idx, 'MIN');
            },
            '_SUM_AVG_MAX_MIN': (exp: any, idx, operate: 'SUM' | 'AVG' | 'MAX' | 'MIN') => {
                let arrCount = 0;
                let count = 1;
                let total = 0;
                let value = undefined;
                let mark = idx;
                while (count > 0) {
                    mark--;
                    if (exp[mark].type == ValueType.operate && exp[mark].val == ',') {
                        count++;
                        total++;
                    } else {
                        let valueType = Object.prototype.toString.call(exp[mark].val);
                        switch (operate) {
                            case 'SUM':
                            case 'AVG':
                                if (value == undefined) value = 0;
                                if (valueType == '[object Array]') {
                                    for (let i = 0; i < exp[mark].val.length; i++) {
                                        value = this.accAdd(value, this.parseValue(exp[mark].val[i]));
                                    }
                                    arrCount += (<any>exp[mark].val).length - 1;
                                } else {
                                    value = this.accAdd(value, <any>exp[mark].val);
                                }
                                break;
                            case 'MAX':
                                if (valueType == '[object Array]') {
                                    for (let i = 0; i < (<any>exp[mark]).length; i++) {
                                        if (value == undefined) {
                                            value = this.parseValue(exp[mark].val[i]);
                                        }
                                        if (this.parseValue(exp[mark].val[i]) > value) {
                                            value = this.parseValue(exp[mark].val[i]);
                                        }
                                    }
                                } else {
                                    if (value == undefined) {
                                        value = this.parseValue(<any>exp[mark].val);
                                    }
                                    if (this.parseValue(<any>exp[mark].val) > value) {
                                        value = this.parseValue(<any>exp[mark].val);
                                    }
                                }
                                break;
                            case 'MIN':
                                if (valueType == '[object Array]') {
                                    for (let i = 0; i < (<any>exp[mark].val).length; i++) {
                                        if (value == undefined) {
                                            value = this.parseValue(exp[mark][i].val);
                                        }
                                        if (this.parseValue(exp[mark].val[i]) < value) {
                                            value = this.parseValue(exp[mark].val[i]);
                                        }
                                    }
                                } else {
                                    if (value == undefined) {
                                        value = this.parseValue(<any>exp[mark].val);
                                    }
                                    if (this.parseValue(<any>exp[mark].val) < value) {
                                        value = this.parseValue(<any>exp[mark].val);
                                    }
                                }
                                break;
                        }
                        count--;
                    }
                }
                switch (operate) {
                    case 'AVG':
                        value = this.accDiv(<number>value, total + arrCount + 1);
                        break;
                }
                let allCount = (total + 1) * 2;
                exp.splice(idx - (allCount - 1), allCount, {type: ValueType.num, val: value});
                return idx - allCount;
            },
            'ROUND(': (exp: any, idx) => {
                let residx = idx - 4;
                let value = String(exp[idx - 3].val);
                let round = Number(exp[idx - 2].val);
                let potIdx = value.indexOf('.');
                if (round >= 0) {
                    if (potIdx == -1 || round > value.length - potIdx - 2) {
                        exp.splice(idx - 3, 4, {type: ValueType.num,val:Number(value)});
                        return residx;
                    }
                    if (Number(value[potIdx + round + 1]) >= 5) {
                        let addVal = Number(value) > 0 ? Math.pow(0.1, round) : -Math.pow(0.1, round);
                        exp.splice(idx - 3, 4, {type: ValueType.num,val: Number(value.substring(0, potIdx + round + 1)) + addVal});
                        return residx;
                    } else {
                        exp.splice(idx - 3, 4, {type: ValueType.num,val: Number(value.substring(0, potIdx + round + 1))});
                        return residx;
                    }
                } else {
                    if (Number(value) > 0) {
                        value = '+' + value;
                    }
                    if (potIdx == -1) {
                        value = value + '.0';
                    }
                    potIdx = value.indexOf('.');
                    let absRound = Math.abs(round);
                    if (potIdx - 1 <= absRound) {
                        exp.splice(idx - 3, 4, {type: ValueType.num,val: 0});
                        return residx;
                    } else {
                        let addVal = Math.pow(10, -round);
                        if (Number(value[idx + round]) >= 5) {
                            if (Number(value) > 0) {
                                exp.splice(idx - 3, 4, {type: ValueType.num,val: Number(value.substring(0, idx + round)) * addVal + addVal});
                            } else {
                                exp.splice(idx - 3, 4, {type: ValueType.num,val: Number(value.substring(0, idx + round)) * addVal - addVal});
                            }
                            return residx;
                        } else {
                            exp.splice(idx - 3, 4, {type: ValueType.num,val: Number(value.substring(0, idx + round)) * addVal});
                            return residx;
                        }
                    }
                }
            },
            'ROUNDUP(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_ROUND_UP_DOWN'].call(null, <any>exp, idx, 'UP');
            },
            'ROUNDDOWN(': (exp: TExpArr, idx) => {
                return this.OperatePriority['50']['_ROUND_UP_DOWN'].call(null, <any>exp, idx, 'DOWN');
            },
            '_ROUND_UP_DOWN': (exp: any, idx: number, operate: 'DOWN' | 'UP') => {
                let residx = idx - 4;
                let value = String(exp[idx - 3].val);
                let round = Number(exp[idx - 2].val);
                let strArr = value.split('.');
                if (strArr[1] == undefined) {
                    strArr[1] = '0';
                }
                if (round > 0) {
                    if (round > strArr[1].length) {
                        exp.splice(idx - 3, 4, {type: ValueType.num, val: Number(strArr[0] + '.' + strArr[1])});
                        return residx;
                    } else {
                        let val = Number(strArr[1].substring(round));
                        let result = 0;
                        if (val > 0) {
                            if (Number(strArr[0]) >= 0) {
                                if (operate == 'UP') {
                                    result = Number(strArr[0] + '.' + strArr[1].substr(0, round)) + Math.pow(0.1, round);
                                } else if (operate == 'DOWN') {
                                    result = Number(strArr[0] + '.' + strArr[1].substr(0, round))
                                }
                            } else {
                                if (operate == 'UP') {
                                    result = Number(strArr[0] + '.' + strArr[1].substr(0, round)) - Math.pow(0.1, round);
                                } else if (operate == 'DOWN') {
                                    result = Number(strArr[0] + '.' + strArr[1].substr(0, round))
                                }
                            }
                        } else {
                            result = Number(strArr[0] + '.' + strArr[1].substr(0, round));
                        }
                        if (round > 0) {
                            result = Number(result.toFixed(round));
                        } else {
                            result = Number(result.toFixed(0));
                        }
                        exp.splice(idx - 3, 4, {type: ValueType.num, val: result});
                        return residx;
                    }
                } else if (round == 0) {
                    if (operate == 'UP') {
                        if (Number(strArr[1]) > 0) {
                            if (Number(strArr[0]) > 0) {
                                exp.splice(idx - 3, 4, {type: ValueType.num, val: Number(strArr[0]) + 1});
                            } else {
                                exp.splice(idx - 3, 4, {type: ValueType.num, val: Number(strArr[0]) - 1});
                            }
                        } else {
                            exp.splice(idx - 3, 4, {type: ValueType.num, val: Number(strArr[0])});
                            return residx;
                        }
                    } else if (operate == 'DOWN') {
                        exp.splice(idx - 3, 4, {type: ValueType.num, val:Number(strArr[0])});
                    }
                    return residx;
                } else { // round < 0
                    let absRound = Math.abs(round);
                    let resultVal = 0;
                    let len = strArr[0].indexOf('-') == -1 ? strArr[0].length : strArr[0].length - 1;
                    if (len > absRound) {
                        let judgeVal = Number(strArr[0].substring(strArr[0].length - absRound, strArr[0].length) + '.' + strArr[1]);
                        if (operate == 'UP') {
                            if (judgeVal > 0) {
                                if (Number(strArr[0]) > 0) {
                                    resultVal = Number(strArr[0].substring(0, strArr[0].length - absRound)) * Math.pow(10, absRound) + Math.pow(10, absRound);
                                } else {
                                    resultVal = Number(strArr[0].substring(0, strArr[0].length - absRound + 1)) * Math.pow(10, absRound - 1) - Math.pow(10, absRound - 1);
                                }
                            } else {
                                resultVal = Number(strArr[0].substring(0, strArr[0].length - absRound)) * Math.pow(10, absRound);
                            }
                        } else if (operate == 'DOWN') {
                            resultVal = Number(strArr[0].substring(0, strArr[0].length - absRound)) * Math.pow(10, absRound);
                        }
                    } else {
                        if (operate == 'UP') {
                            if (Number(value) > 0) {
                                resultVal = Math.pow(10, absRound);
                            } else if (Number(value) < 0) {
                                resultVal = - Math.pow(10, absRound);
                            } else {
                                resultVal = 0;
                            }
                        } else if (operate == 'DOWN') {
                            resultVal = 0;
                        }
                    }
                    if (round > 0) {
                        resultVal = Number(resultVal.toFixed(round));
                    } else {
                        resultVal = Number(resultVal.toFixed(0));
                    }
                    exp.splice(idx - 3, 4, {type: ValueType.num, val: resultVal});
                    return residx;
                }
            },
            '(': (exp: TExpArr, idx) => {
                return idx;
            },
            ')': (exp: TExpArr, idx) => {
                return idx;
            },
            '{': (exp: any[], idx: number) => {
                let count = 1;
                let mark = idx - 1;
                let result: any = {type: ValueType.array, val: []};
                let total = -1;
                while (count > 0) {
                    if (exp[mark].type == ValueType.operate && exp[mark].val == ',') {
                        count++;
                        total = count;
                    } else {
                        count--;
                        result.val.push(exp[idx - total - count]);
                    }
                    mark--;
                }
                exp.splice(idx - (total * 2 - 1), total * 2, <any>result);
                return idx - 3;
            },
            '}': (exp: TExpArr, idx) => {
                return idx;
            },
            'COUNTIF(': (exp: any, idx) => {
                let count = 0;
                for (let i = idx - 1; i > 0; i--) {
                    if (exp[i].type == ValueType.operate &&exp[i].val == ',') {
                        count++;
                    } else {
                        break;
                    }
                }
                let condition = exp[idx - count - 1];
                
                if(condition.type != ValueType.array) {
                    condition = {
                        type: ValueType.array,
                        val: [condition]
                    }
                }
                let countRes: any = { type: ValueType.array, val: []}
                for(let cond of condition.val) {
                    let tempCount = 0;
                    for (let i = count; i > 0; i--) {
                        if(this.compare(exp[idx - i - count - 1], cond) == true) {
                            tempCount++;
                        }
                    }
                    countRes.val.push({
                        type: ValueType.num,
                        val: tempCount
                    });
                }
                exp.splice(idx - (count + 1) * 2 + 1, (count + 1) * 2, countRes);
                return idx;
            }
        },
        '60': {
            '&': (exp: TExpArr, idx) => {
                let result = String(exp[idx-2]) + String(exp[idx - 1]);
                exp.splice(idx - 2, 3, result);
                return idx - 3;
            }
        }
    }
    allOperate: string[] = [];
    pripority: { [key: string]: string[] } = {};
    constructor() {
        this.resetPriporty();
    }
    public resetPriporty() {
        for (let pripority in this.OperatePriority) {
            this.pripority[pripority] = [];
            for (let operate in this.OperatePriority[pripority]) {
                this.pripority[pripority].push(operate);
                if (/^[_]/.test(operate) == false) {
                    this.allOperate.push(operate);
                }
            }
        }
    }
    // 1 > 0
    compare(value: { type: ValueType, val: any}, condition: { type: ValueType, val: any} | any) {
        let resTrue =  {type: ValueType.boolean, val: true}, resFalse = {type: ValueType.boolean, val: false};
        if(condition.type == ValueType.num) {
            if(value.type != ValueType.num) {
                return resFalse;
            }else{
                if(value.val == condition.val) {
                    return resTrue;
                }else{
                    return resFalse;
                }
            }
        }
        if(condition.type == ValueType.boolean) {
            console.warn('还没有实现boolean值数组判断');
        }
        if(condition.type ==  ValueType.str) {
            if(/^[>=<]{1}/.test(condition.val) == true) {
                let cutPosition = 1;
                if ((<string>condition)[1] == '=') {
                    cutPosition = 2;
                }
                let judgSymbe = condition.val.substring(0, cutPosition);
                let compareVal = condition.val.substring(cutPosition);
                if (judgSymbe == '>') {
                    return value.val > Number(compareVal);
                }
                if (judgSymbe == '>=') {
                    return value.val >= Number(compareVal);
                }
                if (judgSymbe == '<') {
                    return value.val < Number(compareVal);
                }
                if (judgSymbe == '<=') {
                    return value.val <= Number(compareVal);
                }
                if (judgSymbe == '==') {
                    return value.val == String(compareVal);
                }                
            } else {

            }
        }
        console.warn('出现了未知判断情况！');
    }
    protected parseValue(value: string | number): number {
        if (value == '') {
            return 0;
        } else {
            return Number(value);
        }
    }
    protected beforeParse(express: string) {

    }
    protected parseString(express: string, index: number, res: (string|{type: any, val: any})[]) {
        let key: string = express[index];;
        if (key == '"' || key == '\'') {
            key = '';
            index++;
            for (; index < express.length; index++) {
                if (express[index] == '"' || express[index] == '\'') {
                    break;
                }
                key += express[index];
            }
            res.push({
                type: ValueType.str,
                val: key
            });
        }
        return index;
    }
    protected parseOperate(operate: string, express: string, index: number, res: any[]): number{
        if (this.allOperate.indexOf(operate) != -1) {
            let resIdx = this.parseOperate(operate + express[index + 1], express, index + 1, res);
            if (resIdx == -1) {
                res.push({
                    type: ValueType.operate,
                    val: operate
                })
                return index;
            } else {
                return resIdx;
            }
        } else {
            return -1;
        }
    }
    protected parseCell(express: string, index: number, res: any[]) {
        if (/^[A-Z]$/i.test(express[index]) == true) {
            let key = express[index];
            for (let i = index; i < express.length; i++) {
                if (i < express.length - 1) {
                    if (/^[A-Z]{1,2}\d{1,3}$/i.test(key + express[i + 1]) || /^[A-Z]{1,2}$/i.test(key + express[i + 1])) {
                        key += express[i + 1];
                    } else {
                        if (/^[A-Z]{1,2}\d{1,3}$/i.test(key)) {
                            res.push({
                                type: ValueType.cell,
                                val: key
                            });
                            return i;
                        } else {
                            try {
                                if (express[index] + express[index + 1] + express[index + 2] == 'Zzj') {
                                    let prefixStr = '';
                                    let skewing = 0;
                                    if (express.substr(index, 9) == 'ZzjChild!') {
                                        prefixStr = 'ZzjChild!';
                                        skewing = 9;
                                    }
                                    if (express.substr(index, 10) == 'ZzjParent!') {
                                        prefixStr = 'ZzjParent!';
                                        skewing = 10;
                                    }
                                    if (prefixStr !== '') {
                                        if (/^[A-Z]{1,2}\d{1,2}$/i.test(express.substr(index + skewing, 4))) {
                                            res.push({
                                                type: ValueType.cell,
                                                val: prefixStr + express.substr(index + skewing, 4)
                                            })
                                            return index + skewing + 4 - 1;
                                        } else if (/^[A-Z]{1,2}\d{1,2}$/i.test(express.substr(index + skewing, 3))) {
                                            res.push({
                                                type: ValueType.cell,
                                                val: prefixStr + express.substr(index + skewing, 3)
                                            });
                                            return index + skewing + 3 - 1;
                                        } else if (/^[A-Z]{1,2}\d{1,2}$/i.test(express.substr(index + skewing, 2))) {
                                            res.push({
                                                type: ValueType.cell,
                                                val: prefixStr + express.substr(index + skewing, 2)
                                            })
                                            return index + skewing + 2 - 1;
                                        }
                                        return -1;
                                    }
                                } else {
                                    return -1;
                                }
                            } catch (error) {
                                return -1;
                            }
                        }
                    }
                } else {
                    if (/^[A-Z]{1,2}\d{1,3}$/i.test(key)) {
                        res.push({
                            type: ValueType.cell,
                            val: key
                        });
                        return i;
                    } else {
                        return -1;
                    }
                }
            }
        } else {
            return -1;
        }
    }
    protected parseNumber(express: string, index: number, res: any[]) {
        let key = express[index];
        if (/^\d+(\.\d+)?$/.test(key)) {
            if (index < express.length - 1) {
                let i = index;
                for (; i < express.length - 1; i++) {
                    if (/^\d+(\.\d+)?$/.test(key + express[i + 1]) || /^\d+(\.)?$/.test(key + express[i + 1])) {
                        key += express[i + 1];
                    } else {
                        res.push({
                            type: ValueType.num,
                            val: Number(key)
                        });
                        return i;
                    }
                }
                if (/^\d+(\.\d+)?$/.test(key)) {
                    res.push({
                        type: ValueType.num,
                        val: Number(key)
                    });
                    return i;
                }
            } else {
                res.push({
                    type: ValueType.num,
                    val: Number(key)
                });
                return index;
            }
        } else {
            return -1;
        }
    }
    public ParsePlusMinux(express: string, index: number, res: any[]) {
        if ((express[index] == '+' || express[index] == '-')) {
            if (index == 0) {
                if (express[index] == '+') {
                    res.push({
                        type: ValueType.operate,
                        val: 'PLUS('
                    });
                }
                if (express[index] == '-') {
                    res.push({
                        type: ValueType.operate,
                        val: 'MINUX('
                    });
                }
                return index;
            } else if (this.allOperate.indexOf(express[index - 1]) != -1 && express[index - 1] != ')') {
                if (express[index] == '+') {
                    res.push({
                        type: ValueType.operate,
                        val: 'PLUS('
                    });
                }
                if (express[index] == '-') {
                    res.push({
                        type: ValueType.operate,
                        val: 'MINUX('
                    });
                }
                return index;
            }
            return -1;
        } else {
            return - 1;
        }
    }
    public parse(express: string) {
        let res: any[] = [];
        let key = '';
        for (let index = 0; index < express.length && index < 50; index++) {
            key += express[index];
            let changeIndex;
            changeIndex = this.ParsePlusMinux(express, index, res);
            if (changeIndex != -1) {
                index = changeIndex;
                key = '';
                continue;
            }
            changeIndex = this.parseString(express, index, res)
            if (changeIndex != index) {
                index = changeIndex;
                key = '';
                continue;
            }
            changeIndex = this.parseOperate(key, express, index, res);
            if (changeIndex != -1) {
                index = changeIndex;
                key = '';
                continue;
            }
            changeIndex = this.parseCell(express, index, res) as number;
            if (changeIndex != -1) {
                index = changeIndex;
                key = '';
                continue;
            }
            changeIndex = this.parseNumber(express, index, res) as number;
            if (changeIndex != -1) {
                index = changeIndex;
                key = '';
                continue;
            }
        }
        // 处理':'运算符
        let max = res.length;
        let idx = 0;
        while (idx < max) {
            if(res[idx].type == ValueType.operate && res[idx].val == ':') {
                let startCell = TableTools.transformExcelPosionToRowCol(res[idx - 1].val);
                let endCell = TableTools.transformExcelPosionToRowCol(res[idx + 1].val);
                let insertArr: any[] = [];
                for (let i = startCell.row; i <= endCell.row; i++) {
                    for (let j = startCell.col; j <= endCell.col; j++) {
                        let cell = TableTools.transatRowColToExlPosition({ row: i, col: j });
                        insertArr.push({
                            type: ValueType.cell,
                            val: cell
                        });
                        insertArr.push({
                            type: ValueType.operate,
                            val: ','
                        });
                    }
                }
                insertArr.pop();
                res.splice(idx - 1, 3, ...insertArr);
                idx = 0;
                max = res.length;
            } else {
                idx++;
            }
        }
        return res;
    }
    protected getPripority(ch: string): number {
        for (let pripority in this.pripority) {
            if (this.pripority[pripority].indexOf(ch) != -1) return Number(pripority);
        }
        return 0;
    }
    public handleOperate(arr: any[]) {
        let operateArr: any[] = [];
        let expressArr: any[] = [];
        let maxCount = 50;
        for (let item of arr) {
            if (maxCount < 0) {
                throw new Error('maxcount');
            }
            maxCount--;
            if (item.type == ValueType.operate && item.val == ')') {
                while (true) {
                    if (operateArr.length == 0) break;
                    if (this.getPripority(operateArr[operateArr.length - 1].val) == 50 && operateArr[operateArr.length - 1].val != '(') {
                        expressArr.push(operateArr.pop());
                        break;
                    } else {
                        if (operateArr[operateArr.length - 1].val == '(') {
                            operateArr.pop();
                            break;
                        } else {
                            expressArr.push(operateArr.pop());
                        }
                    }
                }
            }
            else if (item.type == ValueType.operate && item.val == '}') {
                while (true) {
                    if (operateArr.length == 0) break;
                    if (this.getPripority(operateArr[operateArr.length - 1].val) == 50 && operateArr[operateArr.length - 1].val != '{') {
                        expressArr.push(operateArr.pop());
                        break;
                    } else {
                        if (operateArr[operateArr.length - 1].val == '{') {
                            expressArr.push(operateArr.pop());
                            break;
                        } else {
                            expressArr.push(operateArr.pop());
                        }
                    }
                }
            } else {
                if (item.type == ValueType.operate && this.allOperate.indexOf(item.val) != -1) {
                    if (operateArr.length == 0) {
                        operateArr.push(item);
                        continue;
                    }
                    let currPropority = this.getPripority(item.val);
                    let i = operateArr.length - 1;
                    if (currPropority == 50) {
                        while (true) {
                            if (operateArr.length <= 0) {
                                operateArr.push(item);
                                break;
                            }
                            if (currPropority >= this.getPripority(operateArr[operateArr.length - 1].val)) {
                                operateArr.push(item);
                                break;
                            } else {
                                expressArr.push(operateArr.pop());
                            }
                        }
                    }
                    else if (currPropority == 40) {
                        while (true) {
                            if (operateArr.length <= 0) {
                                expressArr.push(item);
                                break;
                            };
                            if (operateArr[operateArr.length - 1].val == '(') {
                                operateArr.pop();
                            } else {
                                if (this.getPripority(operateArr[operateArr.length - 1].val) >= 40) {
                                    operateArr.push(item);
                                    break;
                                } else {
                                    expressArr.push(operateArr.pop());
                                }
                            }
                        }
                    }
                    else {
                        while (true) {
                            if (operateArr.length <= 0) {
                                operateArr.push(item);
                                break;
                            }
                            if (currPropority >= this.getPripority(operateArr[operateArr.length - 1].val)) {
                                item.val != '(' && operateArr.push(item);
                                break;
                            } else {
                                if (this.getPripority(operateArr[operateArr.length - 1].val) >= 40) {
                                    item.val != '(' && operateArr.push(item);
                                    break;
                                } else {
                                    expressArr.push(operateArr.pop());
                                }
                            }
                        }
                    }
                } else {
                    expressArr.push(item);
                }
            }
        }
        while (true) {
            if (operateArr.length > 0) {
                expressArr.push(operateArr.pop());
            } else {
                break;
            }
        }
        return expressArr;
    }
    /**
     * 
     * @param arg1 
     * @param arg2
     * @description 除法 
     */
    accDiv(arg1: number, arg2: number) {
        var t1 = 0, t2 = 0, r1, r2;
        try { t1 = arg1.toString().split(".")[1].length } catch (e) { }
        try { t2 = arg2.toString().split(".")[1].length } catch (e) { }
        r1 = Number(arg1.toString().replace(".", ""))
        r2 = Number(arg2.toString().replace(".", ""))
        r1 = Number(arg1.toString().replace(".", ""))
        r2 = Number(arg2.toString().replace(".", ""))
        return this.accMul((r1 / r2), Math.pow(10, t2 - t1));
    }
    /**
     * 
     * @param arg1 
     * @param arg2 
     * @description 乘法
     */
    accMul(arg1: number, arg2: number) {
        let m = 0, s1 = arg1.toString(), s2 = arg2.toString();
        try { m += s1.split(".")[1].length } catch (e) { }
        try { m += s2.split(".")[1].length } catch (e) { }
        return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
    }
    /**
     * 
     * @param arg1 
     * @param arg2 
     * @description 加法
     */
    accAdd(arg1: number, arg2: number) {
        let r1, r2, m;
        try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
        try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
        m = Math.pow(10, Math.max(r1, r2))
        return (arg1 * m + arg2 * m) / m;
    }
    /**
     * 
     * @param arg1 
     * @param arg2 
     * @description 减法
     */
    accSubtr(arg1: number, arg2: number) {
        let r1, r2, m, n;
        try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
        try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
        m = Math.pow(10, Math.max(r1, r2));
        n = (r1 >= r2) ? r1 : r2;
        return Number(((arg1 * m - arg2 * m) / m).toFixed(n));
    }
    public parse2(str: string) {
        let val = this.parse(str);
        return {
            value: this.parse(str),
            handleOperate: () => {
                return this.handleOperate2(val);
            }
        }
    }
    public handleOperate2(expArr: any[]) {
        let val = this.handleOperate(expArr);
        return {
            value: val,
            calcu: () => {
                return this.calcu(val);
            }
        }
    }
    public calcu(exp: (any)[]): string | number | boolean | any[] {
        let maxCycle = 100;
        let len = exp.length;
        for (let i = 0; i < len && exp.length > 1; i++) {
            if(Calculate.mode == 'test') {
              console.log(JSON.parse(JSON.stringify(exp)));  
            }     
            if (maxCycle-- && maxCycle < 1) break;
            if (exp[i].type == ValueType.operate && this.allOperate.indexOf(exp[i].val) != -1) {
                let pripority = this.getPripority(exp[i].val);
                if (this.OperatePriority[pripority].hasOwnProperty(exp[i].val)) {
                    i = this.OperatePriority[pripority][exp[i].val].call(null, exp, i);
                    continue;
                } else {
                    // 出错了
                }
            }
        }
        if(exp[0].type == ValueType.boolean) {
            if(exp[0].val == true) {
                return 'TRUE';
            }else{
                return 'FALSE';
            }
        }
        if(exp[0].type == ValueType.array) {
            if(exp[0].val[0].type != ValueType.boolean) {
                if(exp[0].val[0].type == ValueType.num) {
                    return exp[0].val[0].val;
                }
                return exp[0].val[0].val;
            }else{
                if(exp[0].val[0].val == true) {
                    return 'TRUE';
                }else{
                    return 'FALSE';
                }
            }
        }
        if (exp[0] == '+') {
            return Number(exp[1].val);
        }
        if (exp[0] == '-') {
            return -Number(exp[1].val);
        }
        let returnValye = '';
        if (Object.prototype.toString.call(exp[0].val) == '[object Array]') {
            returnValye = exp[0].val[0];
        } else {
            returnValye = exp[0].val;
        }
        if (<any>returnValye === 0) {
            return returnValye;
        } else {
            return <any>returnValye;
        }
    }
    public getResult(express: string) {

    }
}
export function parse(exp: string) {
    let calArr = Calculate.getInstance().parse(exp);
    return calArr
}
// export function express(cellInstance, value: any, express: string, needCalculate = true) {
//     if (needCalculate == false) {
//         return express;
//     }
//     let calArr = Calculate.getInstance().parse(express);
//     for (let key in calArr) {
//         if(calArr[key].type == ValueType.cell) {
//             let cell = TableTools.transformExcelPosionToRowCol(calArr[key].val)
//             let row = Number(cell.row), col = Number(cell.col);
//             let value = 0;
//             let real = TableTools.transatRealCell(cellInstance.tableIntance.getMergedCells(), { row: row, col: col });
//             try {
//                 value = cellInstance.tableIntance.hot.cellDatas[TableTools.createKey(real.row, real.col)].getNotDecorateData();
//             } catch (error) {
//             }
//             calArr[key] = { type: ValueType.num, val: value};
//         }
//         // if (/^[A-Z]{1,2}\d{1,3}$/i.test(calArr[key])) {
//         //     let cell = TableTools.transformExcelPosionToRowCol(calArr[key])
//         //     let row = Number(cell.row), col = Number(cell.col);
//         //     let value = 0;
//         //     let real = TableTools.transatRealCell(cellInstance.tableIntance.getMergedCells(), { row: row, col: col });
//         //     try {
//         //         value = cellInstance.tableIntance.hot.cellDatas[TableTools.createKey(real.row, real.col)].getNotDecorateData();
//         //     } catch (error) {
//         //     }
//         //     calArr[key] = <any>value;
//         // }
//     }
//     let afterChgOperatePosition = Calculate.getInstance().handleOperate(calArr);
//     return Calculate.getInstance().calcu(afterChgOperatePosition);
// }